home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / emacs-complete / fsf / emacs / lisp / cust-print.el < prev    next >
Text File  |  1994-04-09  |  26KB  |  737 lines

  1. ;;; cust-print.el --- handles print-level and print-circle.
  2.  
  3. ;; Copyright (C) 1992 Free Software Foundation, Inc.
  4.  
  5. ;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
  6. ;; Adapted-By: ESR
  7. ;; Keywords: extensions
  8.  
  9. ;; LCD Archive Entry:
  10. ;; cust-print|Daniel LaLiberte|liberte@cs.uiuc.edu
  11. ;; |Handle print-level, print-circle and more.
  12. ;; |$Date: 1994/04/09 22:24:43 $|$Revision: 2.1.1.1 $|
  13.  
  14. ;; This file is part of GNU Emacs.
  15.  
  16. ;; GNU Emacs is free software; you can redistribute it and/or modify
  17. ;; it under the terms of the GNU General Public License as published by
  18. ;; the Free Software Foundation; either version 2, or (at your option)
  19. ;; any later version.
  20.  
  21. ;; GNU Emacs is distributed in the hope that it will be useful,
  22. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  24. ;; GNU General Public License for more details.
  25.  
  26. ;; You should have received a copy of the GNU General Public License
  27. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  28. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  29.  
  30. ;;; ===============================
  31. ;;; $Header: /home/gd/gnu/emacs/19.0/lisp/RCS/cust-print.el,v 2.1.1.1 1994/04/09 22:24:43 liberte Exp liberte $
  32. ;;; $Log: cust-print.el,v $
  33. ;;; Revision 2.1.1.1  1994/04/09  22:24:43  liberte
  34. ;;; Branch for FSF mods.
  35. ;;;
  36. ;;; Revision 2.1  1994/04/09  22:19:10  liberte
  37. ;;; Jumping up to new revision.
  38. ;;; Simplify definition of defalias for Emacs 18.
  39. ;;;
  40. ;;; Revision 2.1  1994/04/09  22:19:10  liberte
  41. ;;; Jumping up to new revision.
  42. ;;; Simplify definition of defalias for Emacs 18.
  43. ;;;
  44. ;;; Revision 1.14  1994/04/05  21:05:09  liberte
  45. ;;; Change install- and uninstall- to -install and -uninstall.
  46. ;;;
  47. ;;; Revision 1.13  1994/03/24  20:26:05  liberte
  48. ;;; Change "internal" to "original" throughout.
  49. ;;;         (add-custom-printer, delete-custom-printer) replace customizers.
  50. ;;;         (with-custom-print) new
  51. ;;;         (custom-prin1-to-string) Made it more robust.
  52. ;;;
  53. ;;; Revision 1.4  1994/03/23  20:34:29  liberte
  54. ;;; * Change "emacs" to "original" - I just can't decide. 
  55. ;;;
  56. ;;; Revision 1.3  1994/02/21  21:25:36  liberte
  57. ;;; * Make custom-prin1-to-string more robust when errors occur.
  58. ;;; * Change "internal" to "emacs".
  59. ;;;
  60. ;;; Revision 1.2  1993/11/22  22:36:36  liberte
  61. ;;; * Simplified and generalized printer customization.
  62. ;;;     custom-printers is an alist of (PREDICATE . PRINTER) pairs
  63. ;;;     for any data types.  The PRINTER function should print to
  64. ;;;     `standard-output'  add-custom-printer and delete-custom-printer
  65. ;;;     change custom-printers.
  66. ;;;
  67. ;;; * Installation function now called install-custom-print.  The
  68. ;;;     old name is still around for now.
  69. ;;;
  70. ;;; * New macro with-custom-print (added earlier) - executes like
  71. ;;;     progn but with custom-print activated temporarily.
  72. ;;;
  73. ;;; * Cleaned up comments for replacements of standardard printers.
  74. ;;;
  75. ;;; * Changed custom-prin1-to-string to use a temporary buffer.
  76. ;;;
  77. ;;; * Option custom-print-vectors (added earlier) - controls whether
  78. ;;;     vectors should be printed according to print-length and
  79. ;;;     print-length.  Emacs doesnt do this, but cust-print would
  80. ;;;     otherwise do it only if custom printing is required.
  81. ;;;
  82. ;;; * Uninterned symbols are treated as non-read-equivalent.
  83. ;;;
  84.  
  85.  
  86. ;;; Commentary:
  87.  
  88. ;; This package provides a general print handler for prin1 and princ
  89. ;; that supports print-level and print-circle, and by the way,
  90. ;; print-length since the standard routines are being replaced.  Also,
  91. ;; to print custom types constructed from lists and vectors, use
  92. ;; custom-print-list and custom-print-vector.  See the documentation
  93. ;; strings of these variables for more details.  
  94.  
  95. ;; If the results of your expressions contain circular references to
  96. ;; other parts of the same structure, the standard Emacs print
  97. ;; subroutines may fail to print with an untrappable error,
  98. ;; "Apparently circular structure being printed".  If you only use cdr
  99. ;; circular lists (where cdrs of lists point back; what is the right
  100. ;; term here?), you can limit the length of printing with
  101. ;; print-length.  But car circular lists and circular vectors generate
  102. ;; the above mentioned error in Emacs version 18.  Version
  103. ;; 19 supports print-level, but it is often useful to get a better
  104. ;; print representation of circular and shared structures; the print-circle
  105. ;; option may be used to print more concise representations.
  106.  
  107. ;; There are three main ways to use this package.  First, you may
  108. ;; replace prin1, princ, and some subroutines that use them by calling
  109. ;; install-custom-print so that any use of these functions in
  110. ;; Lisp code will be affected; you can later reset with
  111. ;; uninstall-custom-print.  Second, you may temporarily install
  112. ;; these functions with the macro with-custom-print.  Third, you
  113. ;; could call the custom routines directly, thus only affecting the
  114. ;; printing that requires them.
  115.  
  116. ;; Note that subroutines which call print subroutines directly will
  117. ;; not use the custom print functions.  In particular, the evaluation
  118. ;; functions like eval-region call the print subroutines directly.
  119. ;; Therefore, if you evaluate (aref circ-list 0), where circ-list is a
  120. ;; circular list rather than an array, aref calls error directly which
  121. ;; will jump to the top level instead of printing the circular list.
  122.  
  123. ;; Uninterned symbols are recognized when print-circle is non-nil,
  124. ;; but they are not printed specially here.  Use the cl-packages package
  125. ;; to print according to print-gensym.
  126.  
  127. ;; Obviously the right way to implement this custom-print facility is
  128. ;; in C or with hooks into the standard printer.  Please volunteer
  129. ;; since I don't have the time or need.  More CL-like printing
  130. ;; capabilities could be added in the future.
  131.  
  132. ;; Implementation design: we want to use the same list and vector
  133. ;; processing algorithm for all versions of prin1 and princ, since how
  134. ;; the processing is done depends on print-length, print-level, and
  135. ;; print-circle.  For circle printing, a preprocessing step is
  136. ;; required before the final printing.  Thanks to Jamie Zawinski
  137. ;; for motivation and algorithms.
  138.  
  139.  
  140. ;;; Code:
  141. ;;=========================================================
  142.  
  143. ;; If using cl-packages:
  144.  
  145. '(defpackage "cust-print"
  146.    (:nicknames "CP" "custom-print")
  147.    (:use "el")
  148.    (:export
  149.     print-level
  150.     print-circle
  151.  
  152.     custom-print-install
  153.     custom-print-uninstall
  154.     custom-print-installed-p
  155.     with-custom-print
  156.  
  157.     custom-prin1
  158.     custom-princ
  159.     custom-prin1-to-string
  160.     custom-print
  161.     custom-format
  162.     custom-message
  163.     custom-error
  164.  
  165.     custom-printers
  166.     add-custom-printer
  167.     ))
  168.  
  169. '(in-package cust-print)
  170.  
  171. (require 'backquote)
  172.  
  173. ;; Emacs 18 doesnt have defalias.
  174. ;; Provide def for byte compiler.
  175. (eval-and-compile
  176.   (or (fboundp 'defalias) (fset 'defalias 'fset)))
  177.  
  178.  
  179. ;; Variables:
  180. ;;=========================================================
  181.  
  182. ;;(defvar print-length nil
  183. ;;  "*Controls how many elements of a list, at each level, are printed.
  184. ;;This is defined by emacs.")
  185.  
  186. (defvar print-level nil
  187.   "*Controls how many levels deep a nested data object will print.  
  188.  
  189. If nil, printing proceeds recursively and may lead to
  190. max-lisp-eval-depth being exceeded or an error may occur:
  191. `Apparently circular structure being printed.'
  192. Also see `print-length' and `print-circle'.
  193.  
  194. If non-nil, components at levels equal to or greater than `print-level'
  195. are printed simply as `#'.  The object to be printed is at level 0,
  196. and if the object is a list or vector, its top-level components are at
  197. level 1.")
  198.  
  199.  
  200. (defvar print-circle nil
  201.   "*Controls the printing of recursive structures.  
  202.  
  203. If nil, printing proceeds recursively and may lead to
  204. `max-lisp-eval-depth' being exceeded or an error may occur:
  205. \"Apparently circular structure being printed.\"  Also see
  206. `print-length' and `print-level'.
  207.  
  208. If non-nil, shared substructures anywhere in the structure are printed
  209. with `#N=' before the first occurrence (in the order of the print
  210. representation) and `#N#' in place of each subsequent occurrence,
  211. where N is a positive decimal integer.
  212.  
  213. There is no way to read this representation in standard Emacs,
  214. but if you need to do so, try the cl-read.el package.")
  215.  
  216.  
  217. (defvar custom-print-vectors nil
  218.   "*Non-nil if printing of vectors should obey print-level and print-length.
  219.  
  220. For Emacs 18, setting print-level, or adding custom print list or
  221. vector handling will make this happen anyway.  Emacs 19 obeys
  222. print-level, but not for vectors.")
  223.  
  224.  
  225. ;; Custom printers
  226. ;;==========================================================
  227.  
  228. (defconst custom-printers nil
  229.   ;; e.g. '((symbolp . pkg::print-symbol))
  230.   "An alist for custom printing of any type.
  231. Pairs are of the form (PREDICATE . PRINTER).  If PREDICATE is true
  232. for an object, then PRINTER is called with the object.
  233. PRINTER should print to `standard-output' using cust-print-original-princ
  234. if the standard printer is sufficient, or cust-print-prin for complex things.
  235. The PRINTER should return the object being printed.
  236.  
  237. Don't modify this variable directly.  Use `add-custom-printer' and
  238. `delete-custom-printer'")
  239. ;; Should cust-print-original-princ and cust-print-prin be exported symbols?
  240. ;; Or should the standard printers functions be replaced by
  241. ;; CP ones in elisp so that CP internal functions need not be called?
  242.  
  243. (defun add-custom-printer (pred printer)
  244.   "Add a pair of PREDICATE and PRINTER to `custom-printers'.
  245. Any pair that has the same PREDICATE is first removed."
  246.   (setq custom-printers (cons (cons pred printer) 
  247.                   (delq (assq pred custom-printers)
  248.                     custom-printers)))
  249.   ;; Rather than updating here, we could wait until cust-print-top-level is called.
  250.   (cust-print-update-custom-printers))
  251.  
  252. (defun delete-custom-printer (pred)
  253.   "Delete the custom printer associated with PREDICATE."
  254.   (setq custom-printers (delq (assq pred custom-printers)
  255.                   custom-printers))
  256.   (cust-print-update-custom-printers))
  257.  
  258.  
  259. (defun cust-print-use-custom-printer (object)
  260.   ;; Default function returns nil.
  261.   nil)
  262.  
  263. (defun cust-print-update-custom-printers ()
  264.   ;; Modify the definition of cust-print-use-custom-printer
  265.   (defalias 'cust-print-use-custom-printer
  266.     ;; We dont really want to require the byte-compiler.
  267.     ;; (byte-compile
  268.      (` (lambda (object)
  269.       (cond
  270.        (,@ (mapcar (function 
  271.             (lambda (pair)
  272.               (` (((, (car pair)) object) 
  273.                   ((, (cdr pair)) object)))))
  274.                custom-printers))
  275.        ;; Otherwise return nil.
  276.        (t nil)
  277.        )))
  278.      ;; )
  279.   ))
  280.  
  281.  
  282. ;; Saving and restoring emacs printing routines.
  283. ;;====================================================
  284.  
  285. (defun cust-print-set-function-cell (symbol-pair)
  286.   (defalias (car symbol-pair) 
  287.     (symbol-function (car (cdr symbol-pair)))))
  288.  
  289. (defun cust-print-original-princ (object &optional stream)) ; dummy def
  290.  
  291. ;; Save emacs routines.
  292. (if (not (fboundp 'cust-print-original-prin1))
  293.     (mapcar 'cust-print-set-function-cell
  294.         '((cust-print-original-prin1 prin1)
  295.           (cust-print-original-princ princ)
  296.           (cust-print-original-print print)
  297.           (cust-print-original-prin1-to-string prin1-to-string)
  298.           (cust-print-original-format format)
  299.           (cust-print-original-message message)
  300.           (cust-print-original-error error))))
  301.  
  302.  
  303. (defun custom-print-install ()
  304.   "Replace print functions with general, customizable, Lisp versions.
  305. The emacs subroutines are saved away, and you can reinstall them
  306. by running `custom-print-uninstall'."
  307.   (interactive)
  308.   (mapcar 'cust-print-set-function-cell
  309.       '((prin1 custom-prin1)
  310.         (princ custom-princ)
  311.         (print custom-print)
  312.         (prin1-to-string custom-prin1-to-string)
  313.         (format custom-format)
  314.         (message custom-message)
  315.         (error custom-error)
  316.         ))
  317.   t)
  318.   
  319. (defun custom-print-uninstall ()
  320.   "Reset print functions to their emacs subroutines."
  321.   (interactive)
  322.   (mapcar 'cust-print-set-function-cell
  323.       '((prin1 cust-print-original-prin1)
  324.         (princ cust-print-original-princ)
  325.         (print cust-print-original-print)
  326.         (prin1-to-string cust-print-original-prin1-to-string)
  327.         (format cust-print-original-format)
  328.         (message cust-print-original-message)
  329.         (error cust-print-original-error)
  330.         ))
  331.   t)
  332.  
  333. (defalias 'custom-print-funcs-installed-p 'custom-print-installed-p)
  334. (defun custom-print-installed-p ()
  335.   "Return t if custom-print is currently installed, nil otherwise."
  336.   (eq (symbol-function 'custom-prin1) (symbol-function 'prin1)))
  337.  
  338. (put 'with-custom-print-funcs 'edebug-form-spec '(body))
  339. (put 'with-custom-print 'edebug-form-spec '(body))
  340.  
  341. (defalias 'with-custom-print-funcs 'with-custom-print)
  342. (defmacro with-custom-print (&rest body)
  343.   "Temporarily install the custom print package while executing BODY."
  344.   (` (unwind-protect
  345.      (progn
  346.        (custom-print-install)
  347.        (,@ body))
  348.        (custom-print-uninstall))))
  349.  
  350.  
  351. ;; Lisp replacements for prin1 and princ, and for some subrs that use them
  352. ;;===============================================================
  353. ;; - so far only the printing and formatting subrs.
  354.  
  355. (defun custom-prin1 (object &optional stream)
  356.   "Output the printed representation of OBJECT, any Lisp object.
  357. Quoting characters are printed when needed to make output that `read'
  358. can handle, whenever this is possible.
  359. Output stream is STREAM, or value of `standard-output' (which see).
  360.  
  361. This is the custom-print replacement for the standard `prin1'.  It
  362. uses the appropriate printer depending on the values of `print-level'
  363. and `print-circle' (which see)."
  364.   (cust-print-top-level object stream 'cust-print-original-prin1))
  365.  
  366.  
  367. (defun custom-princ (object &optional stream)
  368.   "Output the printed representation of OBJECT, any Lisp object.
  369. No quoting characters are used; no delimiters are printed around
  370. the contents of strings.
  371. Output stream is STREAM, or value of `standard-output' (which see).
  372.  
  373. This is the custom-print replacement for the standard `princ'."
  374.   (cust-print-top-level object stream 'cust-print-original-princ))
  375.  
  376.  
  377. (defun custom-prin1-to-string (object)
  378.   "Return a string containing the printed representation of OBJECT,
  379. any Lisp object.  Quoting characters are used when needed to make output
  380. that `read' can handle, whenever this is possible.
  381.  
  382. This is the custom-print replacement for the standard `prin1-to-string'."
  383.   (let ((buf (get-buffer-create " *custom-print-temp*")))
  384.     ;; We must erase the buffer before printing in case an error 
  385.     ;; occured during the last prin1-to-string and we are in debugger.
  386.     (save-excursion
  387.       (set-buffer buf)
  388.       (erase-buffer))
  389.     ;; We must be in the current-buffer when the print occurs.
  390.     (custom-prin1 object buf)
  391.     (save-excursion
  392.       (set-buffer buf)
  393.       (buffer-string)
  394.       ;; We could erase the buffer again, but why bother?
  395.       )))
  396.  
  397.  
  398. (defun custom-print (object &optional stream)
  399.   "Output the printed representation of OBJECT, with newlines around it.
  400. Quoting characters are printed when needed to make output that `read'
  401. can handle, whenever this is possible.
  402. Output stream is STREAM, or value of `standard-output' (which see).
  403.  
  404. This is the custom-print replacement for the standard `print'."
  405.   (cust-print-original-princ "\n" stream)
  406.   (custom-prin1 object stream)
  407.   (cust-print-original-princ "\n" stream))
  408.  
  409.  
  410. (defun custom-format (fmt &rest args)
  411.   "Format a string out of a control-string and arguments.  
  412. The first argument is a control string.  It, and subsequent arguments
  413. substituted into it, become the value, which is a string.
  414. It may contain %s or %d or %c to substitute successive following arguments.
  415. %s means print an argument as a string, %d means print as number in decimal,
  416. %c means print a number as a single character.
  417. The argument used by %s must be a string or a symbol;
  418. the argument used by %d, %b, %o, %x or %c must be a number.
  419.  
  420. This is the custom-print replacement for the standard `format'.  It
  421. calls the emacs `format' after first making strings for list,
  422. vector, or symbol args.  The format specification for such args should
  423. be `%s' in any case, so a string argument will also work.  The string
  424. is generated with `custom-prin1-to-string', which quotes quotable
  425. characters."
  426.   (apply 'cust-print-original-format fmt
  427.      (mapcar (function (lambda (arg)
  428.                  (if (or (listp arg) (vectorp arg) (symbolp arg))
  429.                  (custom-prin1-to-string arg)
  430.                    arg)))
  431.          args)))
  432.         
  433.   
  434. (defun custom-message (fmt &rest args)
  435.   "Print a one-line message at the bottom of the screen.
  436. The first argument is a control string.
  437. It may contain %s or %d or %c to print successive following arguments.
  438. %s means print an argument as a string, %d means print as number in decimal,
  439. %c means print a number as a single character.
  440. The argument used by %s must be a string or a symbol;
  441. the argument used by %d or %c must be a number.
  442.  
  443. This is the custom-print replacement for the standard `message'.
  444. See `custom-format' for the details."
  445.   ;; It doesn't work to princ the result of custom-format as in:
  446.   ;; (cust-print-original-princ (apply 'custom-format fmt args))
  447.   ;; because the echo area requires special handling
  448.   ;; to avoid duplicating the output.  
  449.   ;; cust-print-original-message does it right.
  450.   (apply 'cust-print-original-message  fmt
  451.      (mapcar (function (lambda (arg)
  452.                  (if (or (listp arg) (vectorp arg) (symbolp arg))
  453.                  (custom-prin1-to-string arg)
  454.                    arg)))
  455.          args)))
  456.         
  457.  
  458. (defun custom-error (fmt &rest args)
  459.   "Signal an error, making error message by passing all args to `format'.
  460.  
  461. This is the custom-print replacement for the standard `error'.
  462. See `custom-format' for the details."
  463.   (signal 'error (list (apply 'custom-format fmt args))))
  464.  
  465.  
  466.  
  467. ;; Support for custom prin1 and princ
  468. ;;=========================================
  469.  
  470. ;; Defs to quiet byte-compiler.
  471. (defvar circle-table)
  472. (defvar cust-print-current-level)
  473.  
  474. (defun cust-print-original-printer (object))  ; One of the standard printers.
  475. (defun cust-print-low-level-prin (object))    ; Used internally.
  476. (defun cust-print-prin (object))              ; Call this to print recursively.
  477.  
  478. (defun cust-print-top-level (object stream emacs-printer)
  479.   ;; Set up for printing.
  480.   (let ((standard-output (or stream standard-output))
  481.     ;; circle-table will be non-nil if anything is circular.
  482.     (circle-table (and print-circle 
  483.                (cust-print-preprocess-circle-tree object)))
  484.     (cust-print-current-level (or print-level -1)))
  485.  
  486.     (defalias 'cust-print-original-printer emacs-printer)
  487.     (defalias 'cust-print-low-level-prin 
  488.       (cond
  489.        ((or custom-printers
  490.         circle-table
  491.         print-level            ; comment out for version 19
  492.         ;; Emacs doesn't use print-level or print-length
  493.         ;; for vectors, but custom-print can.
  494.         (if custom-print-vectors
  495.         (or print-level print-length)))
  496.     'cust-print-print-object)
  497.        (t 'cust-print-original-printer)))
  498.     (defalias 'cust-print-prin 
  499.       (if circle-table 'cust-print-print-circular 'cust-print-low-level-prin))
  500.  
  501.     (cust-print-prin object)
  502.     object))
  503.  
  504.  
  505. (defun cust-print-print-object (object)
  506.   ;; Test object type and print accordingly.
  507.   ;; Could be called as either cust-print-low-level-prin or cust-print-prin.
  508.   (cond 
  509.    ((null object) (cust-print-original-printer object))
  510.    ((cust-print-use-custom-printer object) object)
  511.    ((consp object) (cust-print-list object))
  512.    ((vectorp object) (cust-print-vector object))
  513.    ;; All other types, just print.
  514.    (t (cust-print-original-printer object))))
  515.  
  516.  
  517. (defun cust-print-print-circular (object)
  518.   ;; Printer for `prin1' and `princ' that handles circular structures.
  519.   ;; If OBJECT appears multiply, and has not yet been printed,
  520.   ;; prefix with label; if it has been printed, use `#N#' instead.
  521.   ;; Otherwise, print normally.
  522.   (let ((tag (assq object circle-table)))
  523.     (if tag
  524.     (let ((id (cdr tag)))
  525.       (if (> id 0)
  526.           (progn
  527.         ;; Already printed, so just print id.
  528.         (cust-print-original-princ "#")
  529.         (cust-print-original-princ id)
  530.         (cust-print-original-princ "#"))
  531.         ;; Not printed yet, so label with id and print object.
  532.         (setcdr tag (- id)) ; mark it as printed
  533.         (cust-print-original-princ "#")
  534.         (cust-print-original-princ (- id))
  535.         (cust-print-original-princ "=")
  536.         (cust-print-low-level-prin object)
  537.         ))
  538.       ;; Not repeated in structure.
  539.       (cust-print-low-level-prin object))))
  540.  
  541.  
  542. ;;================================================
  543. ;; List and vector processing for print functions.
  544.  
  545. (defun cust-print-list (list)
  546.   ;; Print a list using print-length, print-level, and print-circle.
  547.   (if (= cust-print-current-level 0)
  548.       (cust-print-original-princ "#")
  549.     (let ((cust-print-current-level (1- cust-print-current-level)))
  550.       (cust-print-original-princ "(")
  551.       (let ((length (or print-length 0)))
  552.  
  553.     ;; Print the first element always (even if length = 0).
  554.     (cust-print-prin (car list))
  555.     (setq list (cdr list))
  556.     (if list (cust-print-original-princ " "))
  557.     (setq length (1- length))
  558.  
  559.     ;; Print the rest of the elements.
  560.     (while (and list (/= 0 length))
  561.       (if (and (listp list)
  562.            (not (assq list circle-table)))
  563.           (progn
  564.         (cust-print-prin (car list))
  565.         (setq list (cdr list)))
  566.  
  567.         ;; cdr is not a list, or it is in circle-table.
  568.         (cust-print-original-princ ". ")
  569.         (cust-print-prin list)
  570.         (setq list nil))
  571.  
  572.       (setq length (1- length))
  573.       (if list (cust-print-original-princ " ")))
  574.  
  575.     (if (and list (= length 0)) (cust-print-original-princ "..."))
  576.     (cust-print-original-princ ")"))))
  577.   list)
  578.  
  579.  
  580. (defun cust-print-vector (vector)
  581.   ;; Print a vector according to print-length, print-level, and print-circle.
  582.   (if (= cust-print-current-level 0)
  583.       (cust-print-original-princ "#")
  584.     (let ((cust-print-current-level (1- cust-print-current-level))
  585.       (i 0)
  586.       (len (length vector)))
  587.       (cust-print-original-princ "[")
  588.  
  589.       (if print-length
  590.       (setq len (min print-length len)))
  591.       ;; Print the elements
  592.       (while (< i len)
  593.     (cust-print-prin (aref vector i))
  594.     (setq i (1+ i))
  595.     (if (< i (length vector)) (cust-print-original-princ " ")))
  596.  
  597.       (if (< i (length vector)) (cust-print-original-princ "..."))
  598.       (cust-print-original-princ "]")
  599.       ))
  600.   vector)
  601.  
  602.  
  603.  
  604. ;; Circular structure preprocessing
  605. ;;==================================
  606.  
  607. (defun cust-print-preprocess-circle-tree (object)
  608.   ;; Fill up the table.  
  609.   (let (;; Table of tags for each object in an object to be printed.
  610.     ;; A tag is of the form:
  611.     ;; ( <object> <nil-t-or-id-number> )
  612.     ;; The id-number is generated after the entire table has been computed.
  613.     ;; During walk through, the real circle-table lives in the cdr so we
  614.     ;; can use setcdr to add new elements instead of having to setq the
  615.     ;; variable sometimes (poor man's locf).
  616.     (circle-table (list nil)))
  617.     (cust-print-walk-circle-tree object)
  618.  
  619.     ;; Reverse table so it is in the order that the objects will be printed.
  620.     ;; This pass could be avoided if we always added to the end of the
  621.     ;; table with setcdr in walk-circle-tree.
  622.     (setcdr circle-table (nreverse (cdr circle-table)))
  623.  
  624.     ;; Walk through the table, assigning id-numbers to those
  625.     ;; objects which will be printed using #N= syntax.  Delete those
  626.     ;; objects which will be printed only once (to speed up assq later).
  627.     (let ((rest circle-table)
  628.       (id -1))
  629.       (while (cdr rest)
  630.     (let ((tag (car (cdr rest))))
  631.       (cond ((cdr tag)
  632.          (setcdr tag id)
  633.          (setq id (1- id))
  634.          (setq rest (cdr rest)))
  635.         ;; Else delete this object.
  636.         (t (setcdr rest (cdr (cdr rest))))))
  637.     ))
  638.     ;; Drop the car.
  639.     (cdr circle-table)
  640.     ))
  641.  
  642.  
  643.  
  644. (defun cust-print-walk-circle-tree (object)
  645.   (let (read-equivalent-p tag)
  646.     (while object
  647.       (setq read-equivalent-p 
  648.         (or (numberp object) 
  649.         (and (symbolp object)
  650.              ;; Check if it is uninterned.
  651.              (eq object (intern-soft (symbol-name object)))))
  652.         tag (and (not read-equivalent-p)
  653.              (assq object (cdr circle-table))))
  654.       (cond (tag
  655.          ;; Seen this object already, so note that.
  656.          (setcdr tag t))
  657.  
  658.         ((not read-equivalent-p)
  659.          ;; Add a tag for this object.
  660.          (setcdr circle-table
  661.              (cons (list object)
  662.                (cdr circle-table)))))
  663.       (setq object
  664.         (cond 
  665.          (tag ;; No need to descend since we have already.
  666.           nil)
  667.  
  668.          ((consp object)
  669.           ;; Walk the car of the list recursively.
  670.           (cust-print-walk-circle-tree (car object))
  671.           ;; But walk the cdr with the above while loop
  672.           ;; to avoid problems with max-lisp-eval-depth.
  673.           ;; And it should be faster than recursion.
  674.           (cdr object))
  675.  
  676.          ((vectorp object)
  677.           ;; Walk the vector.
  678.           (let ((i (length object))
  679.             (j 0))
  680.         (while (< j i)
  681.           (cust-print-walk-circle-tree (aref object j))
  682.           (setq j (1+ j))))))))))
  683.  
  684.  
  685. ;; Example.
  686. ;;=======================================
  687.  
  688. '(progn
  689.    (progn
  690.      ;; Create some circular structures.
  691.      (setq circ-sym (let ((x (make-symbol "FOO"))) (list x x)))
  692.      (setq circ-list (list 'a 'b (vector 1 2 3 4) 'd 'e 'f))
  693.      (setcar (nthcdr 3 circ-list) circ-list)
  694.      (aset (nth 2 circ-list) 2 circ-list)
  695.      (setq dotted-circ-list (list 'a 'b 'c))
  696.      (setcdr (cdr (cdr dotted-circ-list)) dotted-circ-list)
  697.      (setq circ-vector (vector 1 2 3 4 (list 'a 'b 'c 'd) 6 7))
  698.      (aset circ-vector 5 (make-symbol "-gensym-"))
  699.      (setcar (cdr (aref circ-vector 4)) (aref circ-vector 5))
  700.      nil)
  701.  
  702.    (install-custom-print)
  703.    ;; (setq print-circle t)
  704.  
  705.    (let ((print-circle t))
  706.      (or (equal (prin1-to-string circ-list) "#1=(a b [1 2 #1# 4] #1# e f)")
  707.      (error "circular object with array printing")))
  708.  
  709.    (let ((print-circle t))
  710.      (or (equal (prin1-to-string dotted-circ-list) "#1=(a b c . #1#)")
  711.      (error "circular object with array printing")))
  712.  
  713.    (let* ((print-circle t)
  714.       (x (list 'p 'q))
  715.       (y (list (list 'a 'b) x 'foo x)))
  716.      (setcdr (cdr (cdr (cdr y))) (cdr y))
  717.      (or (equal (prin1-to-string y) "((a b) . #1=(#2=(p q) foo #2# . #1#))"
  718.         )
  719.      (error "circular list example from CL manual")))
  720.  
  721.    (let ((print-circle nil))
  722.      ;; cl-packages.el is required to print uninterned symbols like #:FOO.
  723.      ;; (require 'cl-packages)
  724.      (or (equal (prin1-to-string circ-sym) "(#:FOO #:FOO)")
  725.      (error "uninterned symbols in list")))
  726.    (let ((print-circle t))
  727.      (or (equal (prin1-to-string circ-sym) "(#1=FOO #1#)")
  728.      (error "circular uninterned symbols in list")))
  729.  
  730.    (uninstall-custom-print)
  731.    )
  732.  
  733. (provide 'cust-print)
  734.  
  735. ;;; cust-print.el ends here
  736.  
  737.